

#include "RayTracer.hpp"
#include "Math.hpp"
#include "Color.hpp"
#include "Scene.hpp"
#include "Filter.hpp"
#include <iostream>
#include <fstream>
#include <cmath>
#include <cstring>
#include <unistd.h>
#include <sys/time.h>


using namespace com::toxiclabs::tsoc;
using namespace std;



void * render_thread(void * data)
{
	static int id = -1;
	RayTracer * rt;
	
	id++;
	rt = (RayTracer *)data;
	
	rt->RenderThread(id);
}

void * irradiate_thread(void * data)
{
	static int id = -1;
	RayTracer * rt;
	
	id++;
	rt = (RayTracer *)data;
	
	rt->IrradiateThread(id);
}

void * compose_thread(void * data)
{
	static int id = -1;
	RayTracer * rt;
	
	id++;
	rt = (RayTracer *)data;
	
	rt->ComposeThread(id);
}



RayTracer::RayTracer(int width,int height,int threads,Scene * scene,Graph * graph)
{
	this->scene=scene;
	this->graph=graph;
	
	/* read settings */
	this->width=width;
	this->height=height;
	this->num_threads=threads;
	

	 
	//HACK
	shader_ao=false;
	shader_ao_n=16;
	shader_ao_factor=0.3f;
	//HACK
	
	num_photons=128000;
	emitted_photons=0;
	total_emitted_photons=0;
	ppm_radius=0.1;
	ppm_min_bounce=0;
	
	direct_light_samples=4;
	
	//HACK
	camera_pos.x=0;
	camera_pos.y=0;
	camera_pos.z=1;
	camera_pos.w=0;
	//HACK
	
	pthread_mutex_init(&mutex_getchunk,NULL);
	pthread_mutex_init(&mutex_commitchunk,NULL);
	
	timeval t;
	
	gettimeofday(&t,NULL);
	
	random = new Random(t.tv_usec);
	
	//HACK HACK HACK
	fov=45.0f;
		
	
	cout<<"Image size:"<<width<<"x"<<height<<endl;
	
	//creating buffers
	//ToDo: Use c++11 arrays intead of bare metal C arrays
	buffer = new uint32_t[width*height];
	std::memset(buffer,0,width*height*sizeof(uint32_t));
	zbuffer = new float[width*height];
	
	hits = new HitPoint*[width*height];
	std::memset(hits,0,sizeof(HitPoint *));
	
	//Starting filters
	cout<<"* Initializing filters..."<<endl;
	for(int n=0;n<FilterPool::filters.size();n++)
	{
		Filter * filter = FilterPool::filters[n];
		
		cout<<"->"<<filter->name<<endl;
		filter->Init(buffer,zbuffer,width,height);
	}
	
	CreateChunks();
	
	cout<<"* Processing materials"<<endl;
	
	for(int n=0;n<scene->materials.size();n++)
	{
		Material * material = scene->materials[n];
		/*
		material->diffuse_factor=(material->diffuse[0]+material->diffuse[1]+material->diffuse[2])/6.0f;
		material->specular_factor=(material->specular[0]+material->specular[1]+material->specular[2])/6.0f;
		material->absorption_factor=1.0f-(material->diffuse_factor+material->specular_factor);
		*/
		
		//HACK HACK HACK
		material->diffuse_factor=0.5f;
		material->specular_factor=0.1f;
		material->absorption_factor=0.4f;
		
		cout<<"material: "<<material->name<<endl;
		cout<<"df "<<material->diffuse_factor<<endl;
		cout<<"sf "<<material->specular_factor<<endl;
		cout<<"af "<<material->absorption_factor<<endl;
	}
	
	cout<<"* Caching emitters"<<endl;
	for(int n=0;n<scene->triangles.size();n++)
	{
		Material * material = scene->materials[scene->triangles[n]->material_id];
		
		if(material->flags &  TSOC_MATERIAL_EMITTER)
		{
			emitters.push_back(scene->triangles[n]);
		}
	}
	
	if(emitters.size()>0)
		photons_per_surface=num_photons/emitters.size();
	else
		photons_per_surface=0;
	
	cout<<"* Photons per surface:"<<photons_per_surface<<endl;
	
	
}

RayTracer::~RayTracer()
{
	delete buffer;
	delete zbuffer;
}


Chunk * RayTracer::GetChunk()
{
	Chunk * tmp = NULL;
	
	pthread_mutex_lock(&mutex_getchunk);
	if(chunks.size()>0)
	{
		tmp = chunks.back();
		chunks.pop_back();
	}
	pthread_mutex_unlock(&mutex_getchunk);
	
	return tmp;
}

void RayTracer::CreateChunks()
{
	//computing chunk size
	int wchunk=60;
	bool found=false;
	
	while(wchunk<128 && found==false)
	{
		if(width%wchunk==0)found=true;
			else wchunk++;
	}
	  
	 found=false;
	 
	 int hchunk=60;
	 while(hchunk<128 && found==false)
	 {
		 if(height%hchunk==0)found=true;
			else hchunk++;
	 } 
	 
	 cout<<"Chunk size:"<<wchunk<<"x"<<hchunk<<endl;
	
	//preparing chunks
	for(int i=0;i<(width/wchunk);i++)
	{
		for(int j=0;j<(height/hchunk);j++)
		{
			
			Chunk * chunk = new Chunk();
			
			chunk->x=i*wchunk;
			chunk->y=j*hchunk;
			chunk->w=wchunk;
			chunk->h=hchunk;
			
			chunks.push_back(chunk);
			
		}
	}
}

void RayTracer::CommitChunk(Chunk * chunk)
{
	pthread_mutex_lock(&mutex_commitchunk);
	cout<<"* commit chunk ("<<chunk->x<<","<<chunk->y<<")"<<endl;
	delete chunk;
	pthread_mutex_unlock(&mutex_commitchunk);
}

void RayTracer::Irradiate()
{
	pthread_t thread[32];
	
	cout<<"Irradiating with "<<num_threads<<" threads"<<endl;
	
	photon_map.Clear();
	
	
	
	//spawning threads...
	for(int n=0;n<num_threads;n++)
	{
		pthread_create(&thread[n],NULL,irradiate_thread,this);
	}
	
	//waiting for them
	for(int n=0;n<num_threads;n++)
	{
		pthread_join(thread[n],NULL);
		cout<<"thread "<<n<<" dead"<<endl;
	}
	cout<<"completed!"<<endl;
	
	emitted_photons=photon_map.Size();
	total_emitted_photons+=emitted_photons;
	cout<<"-Current emitted photons: "<<emitted_photons<<endl;
	cout<<"-Total emitted photons: "<<total_emitted_photons<<endl;
	
	photon_map.Build();
}

void RayTracer::Render()
{
	pthread_t thread[32];
	
	cout<<"Rendering with "<<num_threads<<" threads"<<endl;
	
	//spawning threads...
	for(int n=0;n<num_threads;n++)
	{
		pthread_create(&thread[n],NULL,render_thread,this);
	}
	
	//waiting for them
	for(int n=0;n<num_threads;n++)
	{
		pthread_join(thread[n],NULL);
		cout<<"thread "<<n<<" dead"<<endl;
	}
	cout<<"completed!"<<endl;
	
}

void RayTracer::IrradiateThread(int id)
{
	
	
	
	vector<Photon *> photons;
	
	cout<<"Thread "<<id<<" enter irradiating"<<endl;
	
	/* simple as hell environment light */
	
	Matrix matrix = scene->cameras[0]->matrix;
	
	
	for(int n=0;n<num_photons;n++)
	{
		Vector origin;
		Vector direction;
		Vector target;
		
		Color energy(5200.0f);
		float radius = 32.0f;
		float r1 = random->GetFloat();
		float r2 = random->GetFloat();
		float theta = r1 * M_PI;
		float phi = r2 * 2.0f * M_PI;
		
		origin.x=radius * sin(theta)*cos(phi);
		origin.z=radius * cos(theta);
		origin.y=radius * sin(theta)*sin(phi);
		origin.w=1;
		
		origin = origin * matrix;
		origin.Homogeneus();

		
		r2 = random->GetFloat();
		phi = r2 * 2.0f * M_PI;
		target.x=radius*cos(phi);
		target.z=0;
		target.y=radius*sin(phi);
		target.w=1;
		
		target = target * matrix;
		target.Homogeneus();
			
			
		direction=target-origin;
		direction.Normalize();
		
				
		energy=energy*200.0f;
		
		PhotonCast(origin,direction,energy,0,&photons);
		
	}
		
		
	
			
	
	
	
	vector<Triangle *>::iterator tit;
	for(tit=emitters.begin();tit!=emitters.end();tit++)
	{
		Triangle * triangle = *tit;
		Vector origin;
		Vector direction;
		Color energy;
		Vector offset;
		
		for(int n=0;n<photons_per_surface;n++)
		{
			float sr1=sqrt(random->GetFloat());
			float r2=random->GetFloat();
			origin.x=(1.0f-sr1)*triangle->vertex[0].x + (sr1*(1.0f-r2))*triangle->vertex[1].x + (sr1*r2)*triangle->vertex[2].x;
			origin.y=(1.0f-sr1)*triangle->vertex[0].y + (sr1*(1.0f-r2))*triangle->vertex[1].y + (sr1*r2)*triangle->vertex[2].y;
			origin.z=(1.0f-sr1)*triangle->vertex[0].z + (sr1*(1.0f-r2))*triangle->vertex[1].z + (sr1*r2)*triangle->vertex[2].z;
			origin.w=1.0f;				
			
			double r[2];
			gsl_qrng_get (scene->materials[triangle->material_id]->light->q_direction, r);
			direction=PerturbateNormal(triangle,0.98f,r[0],r[1]);
			
			//direction=PerturbateNormal(triangle,0.98f);
			
			/*
			int r3;
			
			r3 = random->GetInteger()%(width*height);
			while(hits[r3]==NULL)
			{
				r3 = random->GetInteger()%(width*height);
			}
			direction=hits[r3]->position-origin;	
			direction.Normalize();			
			*/			
			offset = direction * 0.05f;
			origin = origin + offset;
						
			energy = scene->materials[triangle->material_id]->light->color * scene->materials[triangle->material_id]->light->power;
			
			PhotonCast(origin,direction,energy,0,&photons);
			
			
		}
	}
	
	photon_map.AddPhotons(&photons);
	
	
	
	
	cout<<"Irradiating thread  "<<id<<" completed!"<<endl;
	
}

void RayTracer::RenderThread(int id)
{
	cout<<"Thread "<<id<<" enter rendering"<<endl;
	
	int n=0;
	
	float Z;
	Vector origin;
	Vector direction;
	
	float beta;
	float v;
	float fwidth,fheight;
	float pw,ph;
	 
	beta = fov/2.0f;
	v = camera_pos.z * tan(DegToRad(beta));
	 
	//far plane dimensions
	fheight = v * 2.0f;
	//HACK HACk HACK
	fwidth = fheight * 1.333f; //Aspect ratio
	 
	//pixel dimensions
	pw = fwidth/width;
	ph = fheight/height;
	 
	 
	 Chunk * chunk = GetChunk();
	 
	 while(chunk!=NULL)
	 {
		 		 
		 
		 for(int j=chunk->y;j<(chunk->y+chunk->h);j++)
		 {
			for(int i=chunk->x;i<(chunk->x+chunk->w);i++)
			{
				
				origin.Set(camera_pos.x,camera_pos.y,-camera_pos.z,1.0f);
				
				direction.Set((i*pw)+(pw*0.5f)-(width*pw*0.5f),(ph*0.5f)+(height*ph*0.5f)-(j*ph),camera_pos.z,0.0f);
				direction.Normalize();
						
				
				HitPoint * hit = RayCast(origin,direction);
				
				hits[i+j*width]=hit;
				
				if(hit!=NULL)
				{
					Color z(1.0f-hit->position.z/10.0f,1.0f-hit->position.z/10.0f,1.0f-hit->position.z/10.0f,1.0);
					PutPixel(i,j,z.Pixel(),0.0f);
				}
				
			}
		 }
		 
		
		 n++;
		
		 CommitChunk(chunk);
		 chunk = GetChunk();
	 }
 
	
	cout<<"Thread "<<id<<" is ready"<<endl;
	cout<<"Thread "<<id<<" processed "<<n<<" chunks"<<endl;	
}

void RayTracer::PhotonCast(Vector &origin,Vector &direction,Color &power,int bounce,std::vector<Photon *> * photons)
{
	vector<Triangle *> triangles;
	//HACK HACk HACK
	float depth=1000.0f;
	float tmp;
	bool found=false;
	Vector collision;
	Vector collision_tmp;
	Vector co;
	Triangle * triangle_match;
	Vector normal;
	
	Color diffuse;
	
	
	
	graph->Traverse(origin,direction,&triangles);
	
	vector<Triangle *>::iterator it;			
	
	for(it=triangles.begin();it!=triangles.end();it++)
	{
		Triangle * triangle=*it;
	
		if(Graph::RayTriangle(triangle,origin,direction,&collision_tmp))
		{
			found=true;
			co = collision_tmp - origin;
			tmp=co.Module();
			
			if(tmp<depth)
			{	
				depth=tmp;
				triangle_match=triangle;
				
				collision=collision_tmp;
			}
		}
			
		
	}
	
	/* back face collisions are discarded */
	
	if(found)
	{
		float pn = triangle_match->normal[0] * direction;
		
		if(pn>0.0)return;
	}
	

	/* get material color */
	if(found)
	{
		
		Vector uvw;
		float u,v;
		Color texel;
		
		uvw = GetUVW(triangle_match,collision);
		
		Vector trNormal = triangle_match->pnormal;

		
		if(scene->materials[triangle_match->material_id]->flags & TSOC_MATERIAL_TEXTURE_COLOR)
		{
			   
			u = (triangle_match->texture[0].u*uvw[0]) + (triangle_match->texture[1].u*uvw[1]) + (triangle_match->texture[2].u*uvw[2]);
			v = (triangle_match->texture[0].v*uvw[0]) + (triangle_match->texture[1].v*uvw[1]) + (triangle_match->texture[2].v*uvw[2]);
			texel=scene->materials[triangle_match->material_id]->texture_kd->Texel(u,v);
		   
			diffuse=texel;
			   
		}
		else
		{
			diffuse=scene->materials[triangle_match->material_id]->diffuse;
		}
	
	
	
	
		/* normal interpolation */
		
		Vector n1;
		Vector n2;
		Vector n3;
		
		n1 = triangle_match->normal[0] * uvw[0];
		n2 = triangle_match->normal[1] * uvw[1];
		n3 = triangle_match->normal[2] * uvw[2];
		normal = n1 + n2 + n3;
		normal.Normalize();	
		
		
		
			
	}


	if(found)
	{
		/* roussian roulete */
		float r1 = random->GetFloat();
		Vector R;
		Vector offset;
		
		
		float cosAlpha = -(direction * normal);
		
		
		Material * material = scene->materials[triangle_match->material_id];
		
		if(r1<material->diffuse_factor)
		{
			
			/* store and diffuse reflect */
			/* ToDo: Implement proper diffuse reflection */
			
			Photon * photon = new Photon();
			photon->position=collision;
			photon->direction=direction;
			photon->normal=normal;
			
			
			photon->power=power*diffuse;
			
			
			photon->type=TSOC_PHOTON_DIRECT; 
			photons->push_back(photon);
			
			
			R=PerturbateNormal(triangle_match,0.98f);
				
			offset = R * 0.05f;
			collision = collision + offset;					
									
			Color rpower=power*diffuse*material->diffuse_factor*cosAlpha;			
			PhotonCast(collision,R,rpower,bounce+1,photons);
			
		}
		else
		{
			if(bounce==0 && r1>=material->diffuse_factor && r1<(material->diffuse_factor+material->specular_factor))
			{
				/* Specular bounce */
				
				float c1 = -(normal * direction);
				R=normal * (c1*2.0f);
				R=R+direction;
				
				offset = R * 0.05f;				
				collision = collision + offset;		
						
				//ToDo
				Color specular;
				specular=power;
				
				//flags=flags & (~TSOC_PHOTON_PRIMARY);
				PhotonCast(collision,R,specular,bounce+1,photons);
			}
			else
			{
				
				Photon * photon = new Photon();
				
				photon->position=collision;
				photon->direction=direction;
				photon->normal=normal;
				photon->power=power*diffuse;
				
				photon->type=TSOC_PHOTON_DIRECT;
				photons->push_back(photon);
					
				
			}
		}
			
				
	}
	
}


HitPoint * RayTracer::RayCast(Vector &origin,Vector &direction)
{
	float cosAlpha;
	Vector collision;
	Vector collision_tmp;
	Vector voc;
	float depth=1000.0f;
	float tmp;
	
	Vector sink;
	float light_dir[4];
	float light_dist;
	float L[4];
	float I;
	
	float  diffuse[4];
	float  specular[4];
	
	bool path_to_light;
		
	bool found = false;
	
	float pixel[4];
	float Z;
	
	float flux[4];
	float * trNormal;
		
		
	Triangle * triangle;
	Triangle * triangle_match;
	
	vector<Triangle *> triangles;
	
	
	
	
	graph->Traverse(origin,direction,&triangles);
	
	vector<Triangle *>::iterator it;			
	
	for(it=triangles.begin();it!=triangles.end();it++)
	{
		triangle=*it;
		
		if(Graph::RayTriangle(triangle,origin,direction,&collision_tmp))
		{
			found=true;
	
			tmp=collision_tmp.z;
			if(tmp<depth)
			{	
				depth=tmp;
				triangle_match=triangle;
				collision=collision_tmp;
				Z=depth;
			}
		}
		
		
	}
	
	  
		
	
		
	if(found)
	{
				
		HitPoint * hit = new HitPoint();
		hit->triangle=triangle_match;
		hit->uvw=GetUVW(triangle_match,collision);
		hit->radius=ppm_radius;
		hit->N=0;
		hit->position=collision;
		hit->normal=triangle_match->pnormal;
		hit->flux.Set(0.0f,0.0f,0.0f,1.0f);
		hit->direct.Set(0.0f,0.0f,0.0f,1.0f);
		
		
		return hit;
	}
	else
	{
		return NULL;
	}
}


void RayTracer::GetLightContribution(Triangle * tri,Vector &position,Vector &normal,Color * power)
{
	
	
	bool first=true;
	Color energy;
	
	
	vector<Triangle *>::iterator tit;
	
	for(tit=emitters.begin();tit!=emitters.end();tit++)
	{
				
		
		Vector direction;
		Vector origin;
		
		energy.Set(0.0f,0.0f,0.0f,1.0f);
		
		
		vector<Triangle *> triangles;
		
		Triangle * triangle = *tit;
		
		
		for(int n=0;n<direct_light_samples;n++)
		{
			
			float target_dist;
			bool path_to_light;
			
						
			/* generate a random point inside the emitter triangle */
			float sr1=sqrt(random->GetFloat());
			float r2=random->GetFloat();
			origin.x=(1.0f-sr1)*triangle->vertex[0].x + (sr1*(1.0f-r2))*triangle->vertex[1].x + (sr1*r2)*triangle->vertex[2].x;
			origin.y=(1.0f-sr1)*triangle->vertex[0].y + (sr1*(1.0f-r2))*triangle->vertex[1].y + (sr1*r2)*triangle->vertex[2].y;
			origin.z=(1.0f-sr1)*triangle->vertex[0].z + (sr1*(1.0f-r2))*triangle->vertex[1].z + (sr1*r2)*triangle->vertex[2].z;
			
			/* emitter to triangle target direction vector */
			direction = position - origin;
			target_dist=direction.Module();
			direction.Normalize();
			
			
			graph->Traverse(origin,direction,&triangles);
	
			vector<Triangle *>::iterator it;			
			path_to_light=true;
			
			for(it=triangles.begin();it!=triangles.end();it++)
			{
				Vector collision_tmp;
				float dist;
				
				Triangle * triangle_current = *it;
				
				if(triangle_current==triangle || triangle_current==tri)continue;
				
				if(Graph::RayTriangle(triangle_current,origin,direction,&collision_tmp))
				{
					Vector R1;
					
					R1=origin - collision_tmp;
					dist=R1.Module();
					
					
					if(dist<target_dist)
					{
						path_to_light=false;
						break;
					}
				}
			
				
			}
			
			if(path_to_light)
			{
				Color c_light = scene->materials[triangle->material_id]->light->color;
				float factor = 1.0f/direct_light_samples;
				
				float cosAlpha = -(normal * direction);
				
				if(cosAlpha<0.0f)cosAlpha=0.0f;
				
				//cosAlpha=1.0f;
				
				energy.r+=c_light.r*cosAlpha*factor;
				energy.g+=c_light.g*cosAlpha*factor;
				energy.b+=c_light.b*cosAlpha*factor;
				
												
				
			}
			
			
			
			
		}//samples
		
		
		
		
		if(first)
		{
			first=false;
			*power=energy;
		}
		else
		{
			*power=*power + energy;		
		}
		
	}//emitters
	
	
	
}

void RayTracer::Compose()
{
	cout<<"* Composing...";
	CreateChunks();
		
	pthread_t thread[32];
		
	
	//spawning threads...
	for(int n=0;n<num_threads;n++)
	{
		pthread_create(&thread[n],NULL,compose_thread,this);
	}
	
	//waiting for them
	for(int n=0;n<num_threads;n++)
	{
		pthread_join(thread[n],NULL);
	}
		
		cout<<"done"<<endl;
}


void RayTracer::ComposeThread(int id)
{
	Color background;
			
	
	background.Set(0.0f,0.0f,0.0f,1.0f);
	
	
	Chunk * chunk = GetChunk();

	while(chunk!=NULL)
	{
			  
		for(int j=chunk->y;j<(chunk->y+chunk->h);j++)
		{
			for(int i=chunk->x;i<(chunk->x+chunk->w);i++)
			{
				HitPoint * hit = hits[i+j*width];
				if(hit!=NULL)
				{
					Color pixel;
					Color radiance;
					float N,M;					
					float alpha=0.7f;
					float R;
					float c_factor=0.0f;
					float ratio=1.0f;
					
					float u,v;
					Color texel;
					Color diffuse;
					Vector bump;
					
					/* compute diffuse properties */
					if(scene->materials[hit->triangle->material_id]->flags & TSOC_MATERIAL_TEXTURE_COLOR)
					{
						   
						u = (hit->triangle->texture[0].u*hit->uvw[0]) + (hit->triangle->texture[1].u*hit->uvw[1]) + (hit->triangle->texture[2].u*hit->uvw[2]);
						v = (hit->triangle->texture[0].v*hit->uvw[0]) + (hit->triangle->texture[1].v*hit->uvw[1]) + (hit->triangle->texture[2].v*hit->uvw[2]);
						texel=scene->materials[hit->triangle->material_id]->texture_kd->Texel(u,v);
					   
						diffuse=texel;
						   
					}
					else
					{
						diffuse=scene->materials[hit->triangle->material_id]->diffuse;
					}
					
					/* normal interpolation */
					Vector normal;
					Vector n1;
					Vector n2;
					Vector n3;
									
					/*
					if(scene->materials[hit->triangle->material_id]->flags & TSOC_MATERIAL_TEXTURE_NORMAL)
					{
						u = (hit->triangle->uv[0]*hit->uvw[0]) + (hit->triangle->uv[2]*hit->uvw[1]) + (hit->triangle->uv[4]*hit->uvw[2]);
						v = (hit->triangle->uv[1]*hit->uvw[0]) + (hit->triangle->uv[3]*hit->uvw[1]) + (hit->triangle->uv[5]*hit->uvw[2]);
						scene->materials[hit->triangle->material_id]->texture_normal->Bump(u,v,bump);
						
						float E1[4];
						float E2[4];
						float dUV1[2];
						float dUV2[2];
						
						math::vSub(hit->triangle->vertex,hit->triangle->vertex+4,E1);
						math::vSub(hit->triangle->vertex,hit->triangle->vertex+8,E2);
						
						dUV1[0]=hit->triangle->uv[2]-hit->triangle->uv[0];
						dUV1[1]=hit->triangle->uv[3]-hit->triangle->uv[1];
						
						dUV2[0]=hit->triangle->uv[4]-hit->triangle->uv[0];
						dUV2[1]=hit->triangle->uv[5]-hit->triangle->uv[1];
						
												
						float r = 1.0f/(dUV1[0]*dUV2[1] - dUV1[1]*dUV2[0]);
						
							
						float T[4];
						float B[4];
						
						T[0]=(E1[0]*dUV2[1] - E2[0]*dUV1[1])*r;
						T[1]=(E1[1]*dUV2[1] - E2[1]*dUV1[1])*r;
						T[2]=(E1[2]*dUV2[1] - E2[2]*dUV1[1])*r;				
							
						B[0]=(E2[0]*dUV1[0] - E1[0]*dUV2[0])*r;
						B[1]=(E2[1]*dUV1[0] - E1[1]*dUV2[0])*r;
						B[2]=(E2[2]*dUV1[0] - E1[2]*dUV2[0])*r;
						
						math::vNormalize(T);
						math::vNormalize(B);
												
						math::vCopy(hit->triangle->normal,n1);
						
						
						float m[16];
						
						m[0]=T[0];
						m[1]=B[0];
						m[2]=n1[0];
						m[3]=0.0f;
						
						m[4]=T[1];
						m[5]=B[1];
						m[6]=n1[1];
						m[7]=0.0f;
						
						m[8]=T[2];
						m[9]=B[2];
						m[10]=n1[2];
						m[11]=0.0f;
						
						m[12]=0.0f;
						m[13]=0.0f;
						m[14]=0.0f;
						m[15]=1.0f;
						
						math::vMatrixMult(bump,m,normal);
												
						
						
						math::vNormalize(normal);
						
						
											
					}
					else
					{
						math::vCopy(hit->triangle->normal,n1);
						math::vCopy(hit->triangle->normal+4,n2);
						math::vCopy(hit->triangle->normal+8,n3);
						
						math::vScale(n1,hit->uvw[0]);
						math::vScale(n2,hit->uvw[1]);
						math::vScale(n3,hit->uvw[2]);
						
						math::vAdd(n1,n2,normal);
						math::vAdd(n3,normal,normal);
						
						math::vNormalize(normal);
					}
					*/
					
					n1=hit->triangle->normal[0] * hit->uvw[0];
					n2=hit->triangle->normal[1] * hit->uvw[1];
					n3=hit->triangle->normal[2] * hit->uvw[2];
					
					normal = n1 +n2 +n3;
					normal.Normalize();
					
					
					
					/* Progressive photon mapping */
					
					M = photon_map.Search(hit->position,normal,TSOC_PHOTON_DIRECT,hit->radius,&radiance);
										
					
					N=hit->N;
					R=hit->radius;
					
					if(M==0)goto render;
									
					if(M+N==0)
					{
						hit->flux.Set(0.0f,0.0f,0.0f,1.0f);
						goto render;
					}
					
					
					ratio = (N+(alpha*M))/(N+M);
					
					hit->radius=hit->radius*sqrt(ratio);
					
					hit->N=hit->N+alpha*M;					
					
					
					hit->flux = ((radiance  * diffuse) + hit->flux) * ratio;
					
										
					
					
												
					render:
					
					c_factor=1.0f/(M_PI*R*R*total_emitted_photons);
					
					Color emit(0.0f,0.0f,0.0f,1.0f);
					
					
					if(scene->materials[hit->triangle->material_id]->flags & TSOC_MATERIAL_EMITTER)
					{
						emit=scene->materials[hit->triangle->material_id]->light->color;
					}
																			
					
					
					pixel = (hit->flux * c_factor) +  emit;
											
					
					PutPixel(i,j,pixel.Pixel(),hit->position.z);
				}
				else
					PutPixel(i,j,background.Pixel(),1000.0f);
			}
		}
		
		PostProcess(chunk);
		CommitChunk(chunk);
		chunk = GetChunk();	
	}
}


Vector RayTracer::GetUVW(Triangle * tri,Vector &collision)
{
	
	Vector r;
	
	Vector f1;
	Vector f2;
	Vector f3;
	
	Vector p12;
	Vector p13;
	
	Vector tmp;
	
	float a,a1,a2,a3;
	
	f1 = tri->vertex[0] - collision;//AP
	f2 = tri->vertex[1] - collision;//BC
	f3 = tri->vertex[2] - collision;//CP
	
	p12 = tri->vertex[0] - tri->vertex[1];//AB
	p13 = tri->vertex[0] - tri->vertex[2];//AC
	
	tmp = p12 ^ p13;
	a=tmp.Module();
	
	tmp = f2 ^ f3;
	a1=tmp.Module()/a;
	
	tmp = f3 ^ f1;
	a2=tmp.Module()/a;
	
	tmp = f1 ^ f2;
	a3=tmp.Module()/a;
	
	r.x=a1;
	r.y=a2;
	r.z=a3;
	r.w=1.0f;
		
	return r;
	
}

Vector RayTracer::PerturbateNormal(Triangle * tri,float angle,float r1,float r2)
{
	Vector trNormal;
		
		Vector R1;
		Vector R2;
		Vector pN;
		
		
		float factor1 = M_PI * 2.0f * r1;
		float factor2 =  angle*std::sqrt(r2);
		
		//HACk HACK HACk
		trNormal=tri->normal[0];				
				
		
		float x = std::cos( factor1 ) * factor2;
		float y = std::sin( factor1 ) * factor2;
		float z = std::sqrt( 1.0f - (factor2 * factor2) );
		
		R1 = tri->vertex[0] - tri->vertex[1];
		R2 = R1 ^ trNormal;
		
		R1.Normalize();
		R2.Normalize();
		
		R1 = R1 * x;
		R2 = R2 * y;
		trNormal = trNormal * z;
		
		pN = R1 + R2 + trNormal;
		pN.Normalize();
		
		return pN;
}

Vector RayTracer::PerturbateNormal(Triangle * tri,float angle)
{
	return PerturbateNormal(tri,angle,random->GetFloat(),random->GetFloat());	
}


void RayTracer::PostProcess(Chunk * chunk)
{
	for(int n=0;n<FilterPool::filters.size();n++)
	{
		Filter * filter = FilterPool::filters[n];
		
		filter->Process(chunk->x,chunk->y,chunk->w,chunk->h);
	}

}



void RayTracer::PutPixel(int x,int y,uint32_t pixel,float depth)
{
	
	buffer[x+y*width]=pixel;
	zbuffer[x+y*width]=depth;   
}

void RayTracer::TGADump(const char * filename)
{
	ofstream file(filename,ios::out | ios::binary);
	char * p;
	uint32_t value;
	uint32_t r,g,b,a;
	
	char header[18]={0x0,0x0,0x2, 0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0, 0x20,0x4};
	uint16_t * w = (uint16_t *)(header+12);
	uint16_t * h = (uint16_t *)(header+14);
	
	*w=width;
	*h=height;
	 
	file.write(header,18);
	
	
	for(int j=(height-1);j>=0;j--)
	{
		for(int i=0;i<width;i++)
		{
			value = buffer[i+j*width];
			
			//conversion from rgba to argb (tga)
			r = (value & 0xFF000000)>>8;
			g = (value & 0x00FF0000)>>8;
			b = (value & 0x0000FF00)>>8;
			a = (value & 0x000000FF)<<24;
			
			value = a | r | g | b;
			
			p = (char *)(&value);
			file.write(p,4);
		}
	}
	
	file.close();
	
}
